package com.distribute.framework.core.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.distribute.framework.core.lang.column;
import com.distribute.framework.core.lang.database;
import com.distribute.framework.core.lang.like;
import com.distribute.framework.core.lang.primary;
import com.distribute.framework.core.lang.table;

public class IbatisMap extends HashMap {
	
	private IbatisMap parent=null;
	private Boolean distinct = false;
	
	public IbatisMap() {
		super.put("joins","");
		super.put("definexs","");
		super.put("distinct","");
	}

	private IbatisMap getRoot(){
		return parent==null ? this : parent.getRoot();
	}
	
	@Override
	public Object put(Object key, Object value) {
		if(value!=null && value instanceof IbatisMap && this.get(key)!=null){
			IbatisMap existsIbatisMap = (IbatisMap) this.get(key);
			IbatisMap currentIbatisMap = (IbatisMap) value;
			existsIbatisMap.putAll(currentIbatisMap);
			return value;
		}
		return super.put(key, value);
	}


	@Override
	public Object get(Object key) {
		if(key==null)return null;
		if(key.equals("definexs")){
			return getDefinexs();
		}
		if(key.equals("joins")){
			return getJoins();
		}
		Object value = super.get(key);
		return value;
	}
	private Object getJoins() {
		String strJoins = "";
		Set keySet = this.keySet();
		for(Object key:keySet){
			Object value = super.get(key);
			if(value instanceof IbatisMap){
				IbatisMap map = (IbatisMap)value;
				map.parent = this;
				strJoins+=getJoinsSql(map);
				strJoins+=map.getJoins();
			}
		}
		if(this.distinct)
			this.put("distinct", "distinct");
		return strJoins;
	}
	private Object getDefinexs() {
		String strDefinexs = "";
		Set keySet = this.keySet();
		for(Object key:keySet){
			Object value = super.get(key);
			if(value instanceof IbatisMap){
				IbatisMap map = (IbatisMap)value;
				map.parent = this;
				strDefinexs+=getDefinexsSql(map);
				strDefinexs+=map.getDefinexs();
			}
		}
		if(this.distinct)
			this.put("distinct", "distinct");
		return strDefinexs;
	}
	public String getJoinsSql(Map params){
		String sql = "";
		Class thisClass = this.getClass().getDeclaringClass();//获取当前模型类
		Class modelClass = params.getClass().getDeclaringClass();//获取关联模型类
		String tableName = (params.get("on")==null ? "" : params.get("on")+"_")+getTable(modelClass);
		String thisTable = (this.get("on")==null ? "" : this.get("on")+"_")+getTable(thisClass);
		if(this.haveColumn(thisClass,params.get("on"))){
			if(params.get("to")!=null){
				sql = " left join "+getDatabaseTable(modelClass)+" "+tableName+" on "+thisTable+"."+params.get("on")+"="+tableName+"."+params.get("to")+" ";
			}else{
				sql = " left join "+getDatabaseTable(modelClass)+" "+tableName+" on "+thisTable+"."+params.get("on")+"="+tableName+"."+getPKey(modelClass)+" ";
			}
		}else{
			getRoot().distinct = true;
			sql = " left join "+getDatabaseTable(modelClass)+" "+tableName+" on "+thisTable+"."+getPKey(thisClass)+"="+tableName+"."+params.get("on")+" ";
		}
		if(params.get("definex")!=null)sql+=" and "+params.get("definex");
		return sql;
	}
	

	public String getDefinexsSql(Map params){
		String sql = "";
		Class thisClass = this.getClass().getDeclaringClass();//获取当前模型类
		Class modelClass = params.getClass().getDeclaringClass();//获取关联模型类
		String tableName = (params.get("on")==null ? "" : params.get("on")+"_")+getTable(modelClass);
		String thisTable = (this.get("on")==null ? "" : this.get("on")+"_")+getTable(thisClass);
		Field[] fields = modelClass.getDeclaredFields();
		for(Field field:fields){
			String strSql = "";
			column column = null;
			String name = field.getName();
			if((column = field.getAnnotation(column.class))!=null){
				if(params.get(name+"Start")!=null || params.get(name+"End")!=null){
					if(params.get(name+"Not")!=null)strSql+=" "+params.get(name+"Not")+" ";
					strSql+="(";
					if(params.get(name+"End")!=null)strSql+=" "+tableName+"."+column.value()+" <= "+toSql(params.get(name+"End"),false);
					if(params.get(name+"Start")!=null && params.get(name+"End")!=null)strSql+=" and ";
					if(params.get(name+"Start")!=null)strSql+=" "+tableName+"."+column.value()+" >= "+toSql(params.get(name+"Start"),false);
					strSql+=")";
				}else if(params.get(name+"Max")!=null || params.get(name+"Min")!=null){
					if(params.get(name+"Not")!=null)strSql+=" "+params.get(name+"Not")+" ";
					strSql+="(";
					if(params.get(name+"Max")!=null)strSql+=" "+tableName+"."+column.value()+" <= "+toSql(params.get(name+"Max"),false);
					if(params.get(name+"Max")!=null && params.get(name+"Min")!=null)strSql+=" and ";
					if(params.get(name+"Min")!=null)strSql+=" "+tableName+"."+column.value()+" >= "+toSql(params.get(name+"Min"),false);
					strSql+=")";
				}else if(params.get(name+"In")!=null){
					if(params.get(name+"Not")!=null)strSql+=" "+params.get(name+"Not")+" ";
					strSql+=" "+tableName+"."+column.value()+ " in (" +params.get(name+"In")+")";
				}else if(params.get(name)!=null){
					if(params.get(name+"Not")!=null)strSql+=" "+params.get(name+"Not")+" ";
					String value = (""+params.get(name)).replace("%", "");
					if(value.indexOf(",")>-1 && value.startsWith("[") && value.endsWith("]")){
						value = value.replace("'","''").replace(",", "','").replace("[", "'").replace("]", "'");
						strSql+=" "+tableName+"."+column.value()+ " in (" +value+")";
					}else{
						strSql+=" "+tableName+"."+column.value()+(field.getAnnotation(like.class)!=null?" like ":" = ")+toSql(params.get(name),field.getAnnotation(like.class)!=null);
					}
				}else if(params.get(name+"Not")!=null){
					if(params.get(name+"Not")!=null)strSql+=" "+params.get(name+"Not")+" ";
					strSql+="(";
					strSql+=tableName+"."+column.value()+" is null ";
					if(field.getType()==String.class){
						strSql+=" or length("+tableName+"."+column.value()+")<1";
					}
					strSql+=")";
				}
			}
			if(!strSql.equals(""))sql+=" and "+strSql;		
		}
		if(params.get("definex")!=null)sql+=" and "+params.get("definex");
		return sql;
	}

	private static Object toSql(Object object,Boolean bLike) {
		if(object.getClass().isAssignableFrom(Number.class))
			return object;
		if(object.getClass()==Date.class)
			return "'"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date)object)+"'";
		if(object.getClass()==Boolean.class || object.getClass()==boolean.class)
			return ((Boolean)object)?1:0;
		String strObject = (object.toString().replaceAll("'", "''"));
		if(bLike){
			if(!strObject.startsWith("%"))
				strObject="%"+strObject;
			if(!strObject.endsWith("%"))
				strObject=strObject+"%";
		}
		return "'"+strObject+"'";
	}
	private String getDatabaseTable(Class modelClass) {
		String databasename = "";
		try {
			Method method = modelClass.getMethod("getDataBaseName");
			if(method!=null){
				method.setAccessible(true);
				databasename = (String) method.invoke(null);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		table table = (table) modelClass.getAnnotation(table.class);
		if(databasename.equals("")||databasename==null){
			database database = (database) modelClass.getAnnotation(database.class);
			if(database==null){
				return table.value();
			}else{
				return database.value() + "." + table.value();
			}
		}else{
			return databasename + "." + table.value();
		}
			
	}
	private String getTable(Class modelClass) {
		table table = (table) modelClass.getAnnotation(table.class);
		return table.value();
	}
	private static String getPKey(Class modelClass) {
		for(Field field:modelClass.getDeclaredFields()){
			if(field.getAnnotation(primary.class)!=null){
				column column = field.getAnnotation(column.class);
				return column.value();
			}
		}
		return null;
	}
	private Boolean haveColumn(Class modelClass,Object object) {
		for(Field field:modelClass.getDeclaredFields()){
			column column = field.getAnnotation(column.class);
			if(column!=null && column.value().equals(object)){
				return true;
			}
		}
		return false;
	}
}